home *** CD-ROM | disk | FTP | other *** search
/ Mac Mania 2 / MacMania 2.toast / Demo's / Tools&Utilities / Programming / LList Mgr / LList Mgr For Think C / LList.c next >
Encoding:
C/C++ Source or Header  |  1994-05-22  |  28.5 KB  |  1,154 lines  |  [TEXT/KAHL]

  1. /************************************************************/
  2. /*                                                            */
  3. /* LList.c                                                    */
  4. /*                                                            */
  5. /* Version 1.4, May 1994, added ignoreCase to LLSearch,        */
  6. /*                          added drawNow to LLDoDraw,        */
  7. /*                          added LLSort function                */
  8. /* Version 1.3, April 1994, fixed bug w/no scroll bar,        */
  9. /*                            LLDoDraw(theList, 1) no longer     */
  10. /*                            auto-redraws the list so call     */
  11. /*                            LLUpdate if necessary            */
  12. /* Version 1.2, December 1993, added column frame option,    */
  13. /*                               condenses type if needed,    */
  14. /*                               added LLRect function,        */
  15. /*                               fixed LLAddRow redraw bug    */
  16. /* Version 1.1, November 1993, fixed thumb scrolling bug    */
  17. /* Version 1.0, September 1993                                */
  18. /* Tad Woods, T&T Software, 70312,3552 on CompuServe        */
  19. /*                                                            */
  20. /* See LList.h for function descriptions.                    */
  21. /*                                                            */
  22. /************************************************************/
  23.  
  24. #include "LList.h"
  25.  
  26. /************************************************************/
  27.  
  28. #define LLabs(a) (((a) < 0) ? (-(a)) : (a))
  29. #define LLmax(a,b) (((a) > (b)) ? (a) : (b))
  30. #define LLmin(a,b) (((a) < (b)) ? (a) : (b))
  31.  
  32. /************************************************************/
  33.  
  34. // prototypes
  35.  
  36. LRow *RowNumToRow(LList *theList, short rowNumber);
  37. short RowToRowNum(LRow *row);
  38. void LLHighlight1(LList *theList, LRow *highlightRow);
  39. void InvalRow(LList *theList, LRow *row, short count);
  40. void LLDraw(LList *theList, short drawAllRows, short drawAllSelectedRows);
  41. void LLDelCol1(LList *theList, LRow *row);
  42. void LLDelRow1(LList *theList, LRow *row);
  43. void LLScrollToRowNum(LList *theList, short rowNum);
  44. short CompareData(Ptr data1, Ptr data2, short len1, short len2, short ignoreCase);
  45. short EqualData(Ptr data1, Ptr data2, short len1, short len2, short ignoreCase);
  46.  
  47. /************************************************************/
  48.  
  49. LRow *RowNumToRow(LList *theList, short rowNumber)
  50. {
  51.     LRow        *row;
  52.     
  53.     row = theList->row;
  54.     
  55.     while ((row != NULL) && (rowNumber > 0))
  56.     {
  57.         row = row->nextRow;
  58.         rowNumber--;
  59.     }
  60.     
  61.     return row;
  62. } // RowNumToRow
  63.  
  64. /************************************************************/
  65.  
  66. short RowToRowNum(LRow *row)
  67. {
  68.     short        rowNumber = 0;
  69.     
  70.     while (row != NULL)
  71.     {
  72.         row = row->prevRow;
  73.         rowNumber++;
  74.     }
  75.     
  76.     return rowNumber;
  77. } // RowToRowNum
  78.  
  79. /************************************************************/
  80.  
  81. void LLHighlight1(LList *theList, LRow *highlightRow)
  82. {
  83.     GrafPtr        remPort;
  84.     LRow        *row;
  85.     LColDesc    *colDesc;
  86.     short        rowCount;
  87.     Rect        highlightRect;
  88.     short        loop;
  89.     
  90.     // go through all currently visible rows
  91.     rowCount = 0;
  92.     row = RowNumToRow(theList, theList->firstVisRowNum);
  93.     while ((row != NULL) && (rowCount < theList->numOfVisRows))
  94.     {
  95.         if (row == highlightRow)
  96.         {
  97.             // if drawing is not on this row needs to be drawn later
  98.             if ((theList->dodraw == 0) || (theList->activeFlag == 0))
  99.             {
  100.                 row->needDraw = 1;
  101.                 return;
  102.             }
  103.             
  104.             GetPort(&remPort);
  105.             SetPort(theList->window);
  106.  
  107.             // make highlightRect describe the row's rectangle
  108.             highlightRect.left = theList->view.left;
  109.             highlightRect.right = theList->view.left;
  110.             highlightRect.top = theList->view.top + rowCount * theList->height;
  111.             highlightRect.bottom = highlightRect.top + theList->height;
  112.             
  113.             // go through all columns
  114.             for(loop = 0; loop < theList->numOfColumns; loop++)
  115.             {
  116.                 colDesc = &(theList->colDesc[loop]);
  117.                 
  118.                 // highlight cell            
  119.                 if (!(colDesc->highlight))
  120.                 {
  121.                     // this column is not highlighted, so highlight the preceeding column
  122.                     if (highlightRect.left != highlightRect.right)
  123.                     {
  124.                         *((Byte *)0x0938) &= 0x7F;    // use hilite transfer mode
  125.                         InvertRect(&highlightRect);
  126.                         *((Byte *)0x0938) |= 0x80;
  127.                     }
  128.                     highlightRect.left = highlightRect.right + colDesc->width;
  129.                 }
  130.                 // add this column to the highlight rect
  131.                 highlightRect.right += colDesc->width;
  132.             }
  133.             // highlight whole row at once so it looks good on screen
  134.             if (highlightRect.left != highlightRect.right)
  135.             {
  136.                 *((Byte *)0x0938) &= 0x7F;    // use hilite transfer mode
  137.                 InvertRect(&highlightRect);
  138.                 *((Byte *)0x0938) |= 0x80;
  139.             }
  140.             SetPort(remPort);
  141.             return;
  142.         }    
  143.         row = row->nextRow;
  144.         rowCount++;
  145.     }
  146. } // LLHighlight1
  147.  
  148. /************************************************************/
  149.  
  150. void InvalRow(LList *theList, LRow *row, short count)
  151. {
  152.     LRow        *row2;
  153.     short        rowSearch;
  154.     short        drawFlag;
  155.     
  156.     if (count < 1)
  157.         return;
  158.         
  159.     // start with first visible row
  160.     row2 = RowNumToRow(theList, theList->firstVisRowNum);
  161.     
  162.     // set drawFlag for row and count following rows if they are currently visible
  163.     drawFlag = 0;
  164.     rowSearch = 0;
  165.     while ((row2 != NULL) && (rowSearch < theList->numOfVisRows))
  166.     {
  167.         if ((drawFlag) || (row2 == row))
  168.         {
  169.             row2->needDraw = 1;
  170.             
  171.             count--;
  172.             if (count < 1)
  173.                 drawFlag = 0;
  174.             else 
  175.                 drawFlag = 1;
  176.         }
  177.         
  178.         row2 = row2->nextRow;
  179.         rowSearch++;
  180.     }
  181. } // InvalRow
  182.  
  183. /************************************************************/
  184.  
  185. void LLDraw(LList *theList, short drawAllRows, short drawAllSelectedRows)
  186. {
  187.     GrafPtr        remPort;
  188.     short        remFont;
  189.     short        remSize;
  190.     short        remStyle;
  191.     short        remhSize;
  192.     short        remvSize;
  193.     LRow        *row;
  194.     LCol        *col;
  195.     LColDesc    *colDesc;
  196.     short        rowCount;
  197.     short        width;
  198.     short        nextWidth;
  199.     short        size1;
  200.     short        size2;
  201.     Rect        theRect;
  202.     Rect        highlightRect;
  203.     short        loop;
  204.     
  205.     if (theList->dodraw == 0)
  206.         return;
  207.     
  208.     GetPort(&remPort);
  209.     SetPort(theList->window);
  210.     remFont = theList->window->txFont;
  211.     remSize = theList->window->txSize;
  212.     remStyle = theList->window->txFace;
  213.     TextFont(theList->font);
  214.     TextSize(theList->size);
  215.     
  216.     // go through all currently visible rows
  217.     rowCount = 0;
  218.     row = RowNumToRow(theList, theList->firstVisRowNum);
  219.     while (rowCount < theList->numOfVisRows)
  220.     {
  221.         // make theRect describe the row's rectangle
  222.         theRect.left = theList->view.left;
  223.         theRect.right = theList->view.right;
  224.         theRect.top = theList->view.top + rowCount * theList->height;
  225.         theRect.bottom = theRect.top + theList->height;
  226.  
  227.         if (row == NULL)
  228.         {
  229.             // erase empty row
  230.             EraseRect(&theRect);
  231.         }
  232.         
  233.         // draw and highlight row
  234.         else if ((drawAllRows) || (row->needDraw) || ((drawAllSelectedRows) && (row->selected)))
  235.         {
  236.             // copy theRect to highlightRect
  237.             highlightRect.left = theRect.left;
  238.             highlightRect.right = theRect.left;
  239.             highlightRect.top = theRect.top;
  240.             highlightRect.bottom = theRect.bottom;
  241.             
  242.             // erase the row
  243.             EraseRect(&theRect);
  244.             
  245.             row->needDraw = 0;
  246.             
  247.             // go through all columns
  248.             for(loop = 0; loop < theList->numOfColumns; loop++)
  249.             {
  250.                 colDesc = &(theList->colDesc[loop]);
  251.                 col = &(row->col[loop]);
  252.                 
  253.                 // set right boundary to individual cell size
  254.                 theRect.right = theRect.left + colDesc->width;
  255.  
  256.                 // draw contents of the cell
  257.                 if ((colDesc->dodraw) && (col->data != NULL))
  258.                 {
  259.                     TextFace(colDesc->style);
  260.                     width = 0;
  261.                     size1 = 0;
  262.                     size2 = col->dataLen;
  263.                     while (size1 < size2)
  264.                     {
  265.                         // include next character if it fits within the column width
  266.                         nextWidth = CharWidth(((char *)(col->data))[size1]);
  267.                         if (width + nextWidth < colDesc->width - theList->indent)
  268.                         {
  269.                             width += nextWidth;
  270.                             size1++;
  271.                         }
  272.                         else
  273.                         {
  274.                             // stop if we've already tried condensed type
  275.                             if (theList->window->txFace & 0x0020)
  276.                                 break;
  277.                                 
  278.                             // try to fit again with condensed type
  279.                             TextFace(colDesc->style | 32);
  280.                             width = 0;
  281.                             size1 = 0;
  282.                         }
  283.                         
  284.                     }
  285.                     
  286.                     if (colDesc->justify == 0)        // left justify
  287.                         MoveTo(theRect.left + 2, theRect.bottom - theList->baseline);
  288.                     else if (colDesc->justify > 0)    // center
  289.                         MoveTo(theRect.left + ((colDesc->width - width) / 2), theRect.bottom - theList->baseline);
  290.                     else if (colDesc->justify < 0)    // right justify
  291.                         MoveTo(theRect.right - width - theList->indent, theRect.bottom - theList->baseline);
  292.                     DrawText(col->data, 0, size1);
  293.                 }
  294.  
  295.                 // frame the cell
  296.                 if (colDesc->frame)
  297.                 {
  298.                     remhSize = theList->window->pnSize.h;
  299.                     remvSize = theList->window->pnSize.v;
  300.                     PenSize(1,1);
  301.                     
  302.                     // frame left
  303.                     if ((colDesc->frame & 0x0008) && (loop > 0))
  304.                     {
  305.                         MoveTo(theRect.left, theRect.top-1);
  306.                         LineTo(theRect.left, theRect.bottom-1);
  307.                     }
  308.                     // frame right
  309.                     if (colDesc->frame & 0x0004)
  310.                     {
  311.                         MoveTo(theRect.right, theRect.top-1);
  312.                         LineTo(theRect.right, theRect.bottom-1);
  313.                     }
  314.                     // frame top
  315.                     if (colDesc->frame & 0x0002)
  316.                     {
  317.                         MoveTo(theRect.left, theRect.top-1);
  318.                         LineTo(theRect.right, theRect.top-1);
  319.                     }
  320.                     // frame bottom
  321.                     if (colDesc->frame & 0x0001)
  322.                     {
  323.                         MoveTo(theRect.left, theRect.bottom-1);
  324.                         LineTo(theRect.right, theRect.bottom-1);
  325.                     }
  326.                     PenSize(remhSize,remvSize);
  327.                 }
  328.                 
  329.                 // highlight cell            
  330.                 if (!((theList->activeFlag) && (row->selected) && (colDesc->highlight)))
  331.                 {
  332.                     // this column is not highlighted, so highlight the preceeding column
  333.                     if (highlightRect.left != highlightRect.right)
  334.                     {
  335.                         *((Byte *)0x0938) &= 0x7F;    // use hilite transfer mode
  336.                         InvertRect(&highlightRect);
  337.                         *((Byte *)0x0938) |= 0x80;
  338.                     }
  339.                     highlightRect.left = highlightRect.right + colDesc->width;
  340.                 }
  341.                 // add this column to the highlight rect
  342.                 highlightRect.right += colDesc->width;
  343.                 
  344.                 theRect.left += colDesc->width;
  345.             }
  346.             // highlight whole row at once so it looks good on screen
  347.             if (highlightRect.left != highlightRect.right)
  348.             {
  349.                 *((Byte *)0x0938) &= 0x7F;    // use hilite transfer mode
  350.                 InvertRect(&highlightRect);
  351.                 *((Byte *)0x0938) |= 0x80;
  352.             }
  353.         }    
  354.  
  355.         if (row != NULL)
  356.             row = row->nextRow;
  357.         rowCount++;
  358.     }
  359.     
  360.     // frame the list and draw the scroll bar if there is one
  361.     theRect.top = theList->view.top-1;
  362.     theRect.bottom = theList->view.bottom+1;
  363.     theRect.left = theList->view.left-1;
  364.     theRect.right = theList->view.right+1;
  365.     FrameRect(&theRect);
  366.     if (theList->scroll)
  367.         Draw1Control(theList->scroll);
  368.     
  369.     // restore the original state of the drawing port
  370.     TextFont(remFont);
  371.     TextSize(remSize);
  372.     TextFace(remStyle);
  373.     SetPort(remPort);
  374. } // LLDraw
  375.  
  376. /************************************************************/
  377.  
  378. pascal void LLUpdate(LList *theList)
  379. {
  380.     LLDraw(theList, 1, 0);
  381. } // LLUpdate
  382.  
  383. /************************************************************/
  384.  
  385. pascal void LLDoDraw(LList *theList, short dodraw, short drawNow)
  386. {
  387.     theList->dodraw = dodraw;
  388.     if ((dodraw) && (drawNow))
  389.         LLDraw(theList, 0, 0);
  390. } // LLDoDraw
  391.  
  392. /************************************************************/
  393.  
  394. pascal void LLActivate(LList *theList, short activate)
  395. {
  396.     LRow        *row;
  397.     
  398.     if (activate)
  399.     {
  400.         if (theList->scroll)
  401.             ShowControl(theList->scroll);
  402.         theList->activeFlag = 1;
  403.     }
  404.     else
  405.     {
  406.         if (theList->scroll)
  407.             HideControl(theList->scroll);
  408.         theList->activeFlag = 0;
  409.     }
  410.     LLDraw(theList, 0, 1);
  411. } // LLActivate
  412.  
  413. /************************************************************/
  414.  
  415. pascal LRow *LLNextRow(LList *theList, LRow *row)
  416. {
  417.     if (row == NULL)
  418.         return theList->row;
  419.     else
  420.         return row->nextRow;
  421. } // LLNextRow
  422.  
  423. /************************************************************/
  424.  
  425. pascal LRow *LLPrevRow(LList *theList, LRow *row)
  426. {
  427.     if (row == NULL)
  428.         return RowNumToRow(theList, theList->numOfRows-1);
  429.     else
  430.         return row->prevRow;
  431. } // LLNextRow
  432.  
  433. /************************************************************/
  434.  
  435. pascal LList *LLNew(Rect *view, WindowPtr window, short rowHeight, short numOfColumns, short hasScroll, short selFlags)
  436. {
  437.     LList        *theList;
  438.     Rect        ctrlRect;
  439.     short        loop;
  440.     
  441.     theList = (LList *)NewPtr(sizeof(LList) + (sizeof(LColDesc) * numOfColumns));
  442.     if (theList == NULL)
  443.         return NULL;
  444.     theList->view.top = view->top;
  445.     theList->view.bottom = view->bottom;
  446.     theList->view.left = view->left;
  447.     theList->view.right = view->right;
  448.     theList->window = window;
  449.     theList->font = window->txFont;
  450.     theList->size = window->txSize;
  451.     theList->height = rowHeight;
  452.     theList->baseline = 4;
  453.     theList->indent = 2;
  454.     theList->dodraw = 1;
  455.     theList->selFlags = selFlags;
  456.     theList->activeFlag = 1;
  457.     theList->numOfRows = 0;
  458.     theList->firstVisRowNum = 0;
  459.     theList->numOfVisRows = (view->bottom - view->top) / rowHeight;
  460.     theList->numOfColumns = numOfColumns;
  461.     theList->refCon = 0L;
  462.     theList->row = NULL;
  463.     theList->lastClkRow = NULL;
  464.     theList->lastClkTime = 0L;
  465.     for(loop = 0; loop < numOfColumns; loop++)
  466.     {
  467.         theList->colDesc[loop].width = (view->right - view->left) / numOfColumns;
  468.         theList->colDesc[loop].style = 0;        // plain
  469.         theList->colDesc[loop].justify = 0;        // left
  470.         theList->colDesc[loop].dodraw = 1;        // drawing on
  471.         theList->colDesc[loop].highlight = 1;    // highlight on
  472.         theList->colDesc[loop].frame = 0x0000;    // no frame
  473.     }
  474.     
  475.     if (hasScroll)
  476.     {
  477.         ctrlRect.top = view->top-1;
  478.         ctrlRect.bottom = view->bottom+1;
  479.         ctrlRect.left = view->right;
  480.         ctrlRect.right = view->right+16;
  481.         theList->scroll = NewControl(window, &ctrlRect, "\p", 1, 0, 0, 0, scrollBarProc, (long)theList);
  482.     }
  483.     else
  484.         theList->scroll = NULL;
  485.     
  486.     return theList;
  487. } // LLNew
  488.  
  489. /************************************************************/
  490.  
  491. void LLDelCol1(LList *theList, LRow *row)
  492. {
  493.     short        loop;
  494.  
  495.     // dispose of the data in columns
  496.     for(loop = 0; loop < theList->numOfColumns; loop++)
  497.         if (row->col[loop].data)
  498.             DisposPtr(row->col[loop].data);
  499. } // LLDelCol1
  500.  
  501. /************************************************************/
  502.  
  503. pascal void LLDispose(LList *theList)
  504. {
  505.     LRow        *row;
  506.     short        loop;
  507.     
  508.     // dispose of scroll bar
  509.     if (theList->scroll)
  510.         DisposeControl(theList->scroll);
  511.     
  512.     // dispose of all rows
  513.     while ((row = theList->row) != NULL)
  514.     {
  515.         theList->row = row->nextRow;
  516.         
  517.         // dispose of the data in columns
  518.         LLDelCol1(theList, row);
  519.  
  520.         DisposPtr((Ptr)row);
  521.     }
  522.  
  523.     // dispose of this list
  524.     DisposPtr((Ptr)theList);
  525. } // LLDispose
  526.  
  527. /************************************************************/
  528.  
  529. pascal LRow *LLAddRow(LList *theList, LRow *beforeRow)
  530. {
  531.     LRow        *row;
  532.     short        rowNum;
  533.     short        loop;
  534.     
  535.     row = (LRow *)NewPtr(sizeof(LRow) + (sizeof(LCol) * theList->numOfColumns));
  536.     if (row == NULL)
  537.         return NULL;
  538.     row->nextRow = NULL;
  539.     row->prevRow = NULL;
  540.     row->needDraw = 0;
  541.     row->selected = 0;
  542.     for(loop = 0; loop < theList->numOfColumns; loop++)
  543.         row->col[loop].data = NULL;
  544.     
  545.     if (beforeRow == NULL) {
  546.         if (theList->row == NULL)
  547.         {
  548.             // insert as only row
  549.             theList->row = row;
  550.         }
  551.         else
  552.         {
  553.             // insert row as last row
  554.             beforeRow = theList->row;
  555.             while (beforeRow->nextRow != NULL)
  556.                 beforeRow = beforeRow->nextRow;
  557.             beforeRow->nextRow = row;
  558.             row->prevRow = beforeRow;
  559.         }
  560.     }
  561.     else
  562.     {
  563.         // insert row before an existing row
  564.         row->prevRow = beforeRow->prevRow;
  565.         row->nextRow = beforeRow;
  566.         beforeRow->prevRow = row;
  567.         if (row->prevRow != NULL)
  568.             ((LRow *)(row->prevRow))->nextRow = row;
  569.         else
  570.             // first row in list
  571.             theList->row = row;
  572.     }
  573.     
  574.     // increment total number of rows
  575.     theList->numOfRows++;
  576.     
  577.     // update scroll bar's maximum range
  578.     if (theList->scroll)
  579.         SetCtlMax(theList->scroll, LLmax(theList->numOfRows - theList->numOfVisRows, 0));
  580.     
  581.     rowNum = RowToRowNum(row);
  582.     if (rowNum < theList->firstVisRowNum)
  583.     {
  584.         // compensate when row is added before visible rows
  585.         theList->firstVisRowNum++;
  586.         if (theList->scroll)
  587.             SetCtlValue(theList->scroll, theList->firstVisRowNum);
  588.     }
  589.     else if (rowNum < theList->firstVisRowNum + theList->numOfVisRows)
  590.     {
  591.         // redraw when row is added within visible rows
  592.         InvalRow(theList, row, theList->numOfVisRows);
  593.         LLDraw(theList, 0, 0);
  594.     }
  595.     
  596.     return row;
  597. } // LLAddRow
  598.  
  599. /************************************************************/
  600.  
  601. void LLDelRow1(LList *theList, LRow *row)
  602. {
  603.     short        loop;
  604.     
  605.     theList->numOfRows--;
  606.     if (theList->firstVisRowNum > theList->numOfRows)
  607.         theList->firstVisRowNum = theList->numOfRows;
  608.     InvalRow(theList, row, theList->numOfVisRows);
  609.     if (theList->scroll)
  610.         SetCtlMax(theList->scroll, LLmax(theList->numOfRows - theList->numOfVisRows, 0));
  611.  
  612.     if (row->prevRow == NULL)
  613.         // row was first row in list
  614.         theList->row = row->nextRow;
  615.     else
  616.         ((LRow *)(row->prevRow))->nextRow = row->nextRow;
  617.         
  618.     if (row->nextRow != NULL)
  619.     {
  620.         // row was not the last row
  621.         ((LRow *)(row->nextRow))->prevRow = row->prevRow;
  622.     }
  623.     
  624.     // dispose of the data in columns
  625.     LLDelCol1(theList, row);
  626.     
  627.     if (row->nextRow != NULL)
  628.         InvalRow(theList, row->nextRow, theList->numOfVisRows);
  629.     DisposPtr((Ptr)row);
  630. } // LLDelRow1
  631.  
  632. /************************************************************/
  633.  
  634. pascal void LLDelRow(LList *theList, LRow *row)
  635. {
  636.     if (row == NULL)
  637.     {
  638.         // delete all rows quickly
  639.         row = theList->row;
  640.         while (row != NULL)
  641.         {
  642.             theList->row = row->nextRow;
  643.             LLDelCol1(theList, row);
  644.             DisposPtr((Ptr)row);
  645.             row = theList->row;
  646.         }
  647.         if (theList->scroll)
  648.             SetCtlMax(theList->scroll, 0);
  649.         theList->numOfRows = 0;
  650.         theList->firstVisRowNum = 0;
  651.     }
  652.     else
  653.         LLDelRow1(theList, row);
  654.     LLDraw(theList, 0, 0);
  655. } // LLDelRow
  656.  
  657. /************************************************************/
  658.  
  659. pascal short LLGetSelect(LList *theList, LRow **row, short advanceit)
  660. {
  661.     LRow        *row2;
  662.     
  663.     row2 = *row;
  664.     if (row2 == NULL)
  665.         return 0;
  666.     else if (row2->selected)
  667.         return 1;
  668.     else if (advanceit)
  669.     {
  670.         while ((row2 = row2->nextRow) != NULL)
  671.         {
  672.             if (row2->selected)
  673.             {
  674.                 *row = row2;
  675.                 return 1;
  676.             }
  677.         }
  678.     }
  679.     return 0;
  680. } // LLGetSelect
  681.  
  682. /************************************************************/
  683.  
  684. pascal void LLSetSelect(LList *theList, LRow *row, short setit)
  685. {
  686.     if (row->selected != setit)
  687.     {
  688.         row->selected = setit;
  689.         LLHighlight1(theList, row);
  690.     }
  691. } // LLSetSelect
  692.  
  693. /************************************************************/
  694.  
  695. pascal void LLScroll(LList *theList, short rows)
  696. {
  697.     GrafPtr        remPort;
  698.     Rect        theRect;
  699.     RgnHandle    theUpdateRgn;
  700.     
  701.     // don't scroll past end of list
  702.     if ((theList->firstVisRowNum + theList->numOfVisRows) + rows > theList->numOfRows)
  703.         rows = theList->numOfRows - (theList->firstVisRowNum + theList->numOfVisRows);
  704.         
  705.     // don't scroll past start of list
  706.     if (theList->firstVisRowNum + rows < 0)
  707.         rows = -(theList->firstVisRowNum);
  708.     
  709.     theList->firstVisRowNum += rows;
  710.  
  711.     // call Quickdraw to scroll the rows if we are scrolling less than a whole "page"
  712.     if (LLabs(rows) < theList->numOfVisRows)
  713.     {
  714.         theRect.top = theList->view.top;
  715.         theRect.bottom = theRect.top + (theList->numOfVisRows * theList->height);
  716.         theRect.left = theList->view.left;
  717.         theRect.right = theList->view.right;
  718.         GetPort(&remPort);
  719.         SetPort(theList->window);
  720.         theUpdateRgn = NewRgn();
  721.         ScrollRect(&theRect, 0, -(rows * theList->height), theUpdateRgn);
  722.         DisposeRgn(theUpdateRgn);
  723.         SetPort(remPort);
  724.         if (rows > 0)
  725.             // draw new rows at bottom
  726.             InvalRow(theList, RowNumToRow(theList, theList->firstVisRowNum + theList->numOfVisRows - rows), rows);
  727.         else
  728.             // draw new rows at top
  729.             InvalRow(theList, RowNumToRow(theList, theList->firstVisRowNum), -rows);
  730.     }
  731.     else
  732.         InvalRow(theList, RowNumToRow(theList, theList->firstVisRowNum), theList->numOfVisRows);
  733.         
  734.     if (theList->scroll)
  735.         SetCtlValue(theList->scroll, theList->firstVisRowNum);
  736.     LLDraw(theList, 0, 0);
  737. } // LLScroll
  738.  
  739. /************************************************************/
  740.  
  741. void LLScrollToRowNum(LList *theList, short rowNum)
  742. {
  743.     // converts rows into a value relative to the first visible row
  744.     LLScroll(theList, rowNum - theList->firstVisRowNum);
  745. } // LLScrollToRowNum
  746.  
  747. /************************************************************/
  748.  
  749. pascal void LLScrollToRow(LList *theList, LRow *row)
  750. {
  751.     LRow        *row2;
  752.     short        rowCount;
  753.     
  754.     if (row != NULL)
  755.     {
  756.         rowCount = 0;
  757.         row2 = theList->row;
  758.         while (row2 != NULL)
  759.         {
  760.             if (row2 == row)
  761.             {
  762.                 LLScrollToRowNum(theList, rowCount);
  763.                 return;
  764.             }
  765.             rowCount++;
  766.             row2 = row2->nextRow;
  767.         }
  768.     }
  769.     
  770.     // scroll to end of list
  771.     LLScroll(theList, theList->numOfRows);
  772.     return;
  773. } // LLScrollToRow
  774.  
  775. /************************************************************/
  776.  
  777. pascal void LLScrollAction(ControlHandle scroll, short part)
  778. {
  779.     LList        *theList;
  780.     
  781.     theList = (LList *)((**scroll).contrlRfCon);
  782.     
  783.     switch (part) {
  784.         case inUpButton:
  785.             LLScroll(theList, -1);
  786.             break;
  787.         case inDownButton:
  788.             LLScroll(theList, 1);
  789.             break;
  790.         case inPageUp:
  791.             LLScroll(theList, -(theList->numOfVisRows-1));
  792.             break;
  793.         case inPageDown:
  794.             LLScroll(theList, theList->numOfVisRows-1);
  795.             break;
  796.         default:
  797.             break;
  798.     }
  799. } // LLScrollAction
  800.  
  801. /************************************************************/
  802.  
  803. pascal short LLClick(LList *theList, Point localPoint, short modifiers)
  804. {
  805.     short            controlPart;
  806.     ControlHandle    whichControl;
  807.     LRow            *clickRow;
  808.     long            clickTime;
  809.     short            dblClickFlag;
  810.     LRow            *row;
  811.     short            startRowNum;
  812.     short            stopRowNum;
  813.     short            remRowNum;
  814.     short            rowCount;
  815.     short            highlight;
  816.     short            llNoNilHilite = (theList->selFlags & LLNoNilHilite);
  817.     short            llUseSense = (theList->selFlags & LLUseSense);
  818.     short            llNoExtend = (theList->selFlags & LLNoExtend);
  819.     short            llNoDisjoint = (theList->selFlags & LLNoDisjoint);
  820.     short            llExtendDrag = (theList->selFlags & LLExtendDrag);
  821.     short            llOnlyOne = (theList->selFlags & LLOnlyOne);
  822.     short            shiftDown = (modifiers & 0x0200);
  823.     short            cmdDown = (modifiers & 0x0100);
  824.     
  825.     if ((controlPart = FindControl(localPoint, theList->window, &whichControl)) && (whichControl == theList->scroll))
  826.     {
  827.         if (controlPart == inThumb)
  828.         {
  829.             if (TrackControl(whichControl, localPoint, NULL) == inThumb)
  830.                 LLScrollToRowNum(theList, GetCtlValue(whichControl));
  831.         }
  832.         else
  833.             TrackControl(whichControl, localPoint, &LLScrollAction);
  834.     }
  835.     
  836.     // is the mouse initially inside the list rectangle?
  837.     else if (PtInRect(localPoint, &(theList->view)))
  838.     {
  839.         // get the "time" of the click
  840.         clickTime = TickCount();
  841.  
  842.         // make the clicked in row the startRowNum
  843.         startRowNum = (localPoint.v - theList->view.top) / theList->height + theList->firstVisRowNum;
  844.         clickRow = RowNumToRow(theList, startRowNum);
  845.         
  846.         if ((llNoNilHilite) && (clickRow == NULL))
  847.             return 0;
  848.         
  849.         if (llUseSense)
  850.             highlight = !(LLGetSelect(theList, &clickRow, 0));
  851.         else
  852.             highlight = 1;
  853.  
  854.         // if extending the selection, make the farthest away selected row the startRowNum
  855.         if ((shiftDown) && (!llNoExtend))
  856.         {
  857.             remRowNum = startRowNum;
  858.             rowCount = 0;
  859.             row = theList->row;
  860.             while (row != NULL)
  861.             {
  862.                 if (LLGetSelect(theList, &row, 0))
  863.                 {
  864.                     startRowNum = rowCount;
  865.                     if (startRowNum <= remRowNum)
  866.                         break;
  867.                 }
  868.                 rowCount++;
  869.                 row = row->nextRow;
  870.             }
  871.         }
  872.         
  873.         remRowNum = -1;
  874.         do
  875.         {
  876.             // make the row where the mouse is now the stopRowNum
  877.             stopRowNum = (localPoint.v - theList->view.top) / theList->height;
  878.             if (stopRowNum < 0)
  879.                 stopRowNum = 0;
  880.             else if (stopRowNum > theList->numOfVisRows)
  881.                 stopRowNum = theList->numOfVisRows;
  882.             stopRowNum += theList->firstVisRowNum;
  883.             
  884.             if (stopRowNum != remRowNum)
  885.             {
  886.                 if ((llOnlyOne) || ((!llExtendDrag) && (!shiftDown)) || (llNoDisjoint))
  887.                 {
  888.                     startRowNum = stopRowNum;
  889.                     llNoDisjoint = 0;
  890.                 }
  891.  
  892.                 rowCount = 0;
  893.                 row = theList->row;
  894.                 while (row != NULL)
  895.                 {
  896.                     if ((rowCount >= LLmin(startRowNum, stopRowNum)) && (rowCount <= LLmax(startRowNum, stopRowNum)))
  897.                     {
  898.                         LLSetSelect(theList, row, highlight);
  899.                     }
  900.                     else
  901.                     {
  902.                         if ((llOnlyOne) || ((!llExtendDrag) && (!cmdDown) && (!shiftDown)))
  903.                             LLSetSelect(theList, row, 0);
  904.                     }
  905.                     
  906.                     rowCount++;
  907.                     row = row->nextRow;
  908.                 }
  909.                 
  910.                 // prevent a double-click if there's any dragging
  911.                 dblClickFlag = (remRowNum == -1);
  912.                     
  913.                 remRowNum = stopRowNum;
  914.             }
  915.  
  916.             if (localPoint.v < theList->view.top)
  917.                 // auto scroll up
  918.                 LLScroll(theList, -1);
  919.             else if (localPoint.v > theList->view.bottom)
  920.                 // auto scroll down
  921.                 LLScroll(theList, 1);
  922.  
  923.             GetMouse(&localPoint);
  924.         } while (Button());
  925.         
  926.         // return 1 if double-click occurs in same row within time limit of double-click
  927.         if ((clickRow == theList->lastClkRow) && (clickTime < theList->lastClkTime + GetDblTime()) && (clickRow != NULL) && (!llUseSense))
  928.             return 1;
  929.             
  930.         theList->lastClkRow = clickRow;
  931.         if (dblClickFlag)
  932.             theList->lastClkTime = clickTime;
  933.     }
  934.     return 0;
  935. } // LLClick
  936.  
  937. /************************************************************/
  938.  
  939. pascal void LLGetCell(LList *theList, LRow *row, short colNum, short *dataLen, Ptr data)
  940. {
  941.     if (row->col[colNum].data == NULL)
  942.         *dataLen = 0;
  943.     else 
  944.     {
  945.         *dataLen = LLmin(*dataLen, row->col[colNum].dataLen);
  946.         BlockMove(row->col[colNum].data, data, *dataLen);
  947.     }
  948. } // LLGetCell
  949.  
  950. /************************************************************/
  951.  
  952. pascal void LLSetCell(LList *theList, LRow *row, short colNum, short dataLen, Ptr data)
  953. {
  954.     if (row->col[colNum].data != NULL)
  955.         DisposPtr(row->col[colNum].data);
  956.     
  957.     if (dataLen == 0)
  958.         row->col[colNum].data = NULL;
  959.     else 
  960.     {
  961.         row->col[colNum].data = NewPtr(dataLen);
  962.         if (row->col[colNum].data == NULL)
  963.             row->col[colNum].dataLen = 0;
  964.         else
  965.         {
  966.             row->col[colNum].dataLen = dataLen;
  967.             BlockMove(data, row->col[colNum].data, dataLen);
  968.         }
  969.     }
  970.     InvalRow(theList, row, 1);
  971.     LLDraw(theList, 0, 0);
  972. } // LLSetCell
  973.  
  974. /************************************************************/
  975.  
  976. short CompareData(Ptr data1, Ptr data2, short len1, short len2, short ignoreCase)
  977. {
  978.     char    c1, c2;
  979.     
  980.     if ((len1 == 0) | (len2 == 0))
  981.     {
  982.         if (len1 > 0)
  983.             return 1;    // data1 > data2
  984.         if (len2 > 0)
  985.             return -1;    // data1 < data2
  986.         return 0;        // data1 == data2
  987.     }
  988.     
  989.     len2 = LLmin(len1, len2);
  990.     len1 = 0;
  991.     
  992.     while (len1 < len2)
  993.     {
  994.         c1 = ((char *)data1)[len1];
  995.         c2 = ((char *)data2)[len1];
  996.         if (ignoreCase)
  997.         {
  998.             if ((c1 >= 0x61) && (c1 <= 0x7A)) c1 -= 0x20;
  999.             if ((c2 >= 0x61) && (c2 <= 0x7A)) c2 -= 0x20;
  1000.         }
  1001.         if (c1 > c2)
  1002.             return 1;    // data1 > data2
  1003.         else if (c1 < c2)
  1004.             return -1;    // data1 < data2
  1005.         len1++;
  1006.     }
  1007.     
  1008.     return 0;            // data1 == data2
  1009. } // CompareData
  1010.  
  1011. /************************************************************/
  1012.  
  1013. short EqualData(Ptr data1, Ptr data2, short len1, short len2, short ignoreCase)
  1014. {
  1015.     if (len1 != len2)
  1016.         return 0;
  1017.     else
  1018.         return (CompareData(data1, data2, len1, len2, ignoreCase) == 0);
  1019. } // EqualData
  1020.  
  1021. /************************************************************/
  1022.  
  1023. pascal short LLSearch(LList *theList, LRow **row, short *colFound, short colFirst, short colLast, Ptr data, short dataLen, short ignoreCase)
  1024. {
  1025.     LRow        *row2;
  1026.     short        loop;
  1027.     
  1028.     row2 = *row;
  1029.     colLast = LLmin(colLast, theList->numOfColumns - 1);
  1030.     
  1031.     if (row2 == NULL)
  1032.         row2 = theList->row;
  1033.         
  1034.     while (row2 != NULL)
  1035.     {
  1036.         for(loop = colFirst; loop <= colLast; loop++)
  1037.         {
  1038.             if (EqualData(data, row2->col[loop].data, dataLen, row2->col[loop].dataLen, ignoreCase))
  1039.             {
  1040.                 *row = row2;
  1041.                 *colFound = loop;
  1042.                 return 1;
  1043.             }
  1044.         }
  1045.         row2 = row2->nextRow;
  1046.     }
  1047.     return 0;
  1048. } // LLSearch
  1049.  
  1050. /************************************************************/
  1051.  
  1052. pascal void LLSort(LList *theList, short col1, short col2, short col3, short ignoreCase, short order)
  1053. {
  1054.     LRow        *row1;
  1055.     LRow        *row2;
  1056.     short        swapped = 1;
  1057.     short        result = 0;
  1058.     
  1059.     if (theList->row == NULL)
  1060.         return;
  1061.     
  1062.     if (col1 < 0)
  1063.         return;
  1064.  
  1065.     col1 = LLmin(col1, theList->numOfColumns - 1);
  1066.     col2 = LLmin(col2, theList->numOfColumns - 1);
  1067.     col3 = LLmin(col3, theList->numOfColumns - 1);
  1068.         
  1069.     if ((order != 1) && (order != -1))
  1070.         order = 1;
  1071.  
  1072.     while (swapped)
  1073.     {
  1074.         row1 = theList->row;
  1075.         row2 = row1->nextRow;
  1076.         swapped = 0;
  1077.         
  1078.         while (row2 != NULL)
  1079.         {
  1080.             result = CompareData(row1->col[col1].data, row2->col[col1].data, row1->col[col1].dataLen, row2->col[col1].dataLen, ignoreCase);
  1081.             if ((result == 0) && (col2 >= 0))
  1082.             {
  1083.                 result = CompareData(row1->col[col2].data, row2->col[col2].data, row1->col[col2].dataLen, row2->col[col2].dataLen, ignoreCase);
  1084.                 if ((result == 0) && (col3 >= 0))
  1085.                     result = CompareData(row1->col[col3].data, row2->col[col3].data, row1->col[col3].dataLen, row2->col[col3].dataLen, ignoreCase);
  1086.             }
  1087.             if (result == order)
  1088.             {
  1089.                 if (row1->prevRow == NULL)
  1090.                     theList->row = row2;
  1091.                 else
  1092.                     ((LRow *)(row1->prevRow))->nextRow = row2;
  1093.                 if (row2->nextRow != NULL)
  1094.                     ((LRow *)(row2->nextRow))->prevRow = row1;
  1095.                 row1->nextRow = row2->nextRow;
  1096.                 row2->prevRow = row1->prevRow;
  1097.                 row1->prevRow = row2;
  1098.                 row2->nextRow = row1;
  1099.                 InvalRow(theList, row2, 2);
  1100.                 swapped = 1;
  1101.             }
  1102.             else
  1103.                 row1 = row2;
  1104.  
  1105.             row2 = row1->nextRow;
  1106.         }
  1107.     }
  1108.     LLDraw(theList, 0, 0);
  1109. } // LLSort
  1110.  
  1111. /************************************************************/
  1112.  
  1113. pascal void LLRect(LList *theList, LRow *row, short colFirst, short colLast, Rect *theRect)
  1114. {
  1115.     LRow        *row2;
  1116.     short        row2Count;
  1117.     short        loop;
  1118.     
  1119.     // go through all visible rows
  1120.     row2Count = 0;
  1121.     row2 = RowNumToRow(theList, theList->firstVisRowNum);
  1122.     while ((row2 != NULL) && (row2Count < theList->numOfVisRows))
  1123.     {
  1124.         if (row2 == row)
  1125.         {
  1126.             // set theRect to the visible rectangle of the row and column(s)
  1127.             theRect->left = theList->view.left;
  1128.             theRect->right = theList->view.left;
  1129.             theRect->top = theList->view.top + row2Count * theList->height;
  1130.             theRect->bottom = theRect->top + theList->height;
  1131.  
  1132.             for(loop = 0; loop <= colLast; loop++)
  1133.             {
  1134.                 if (loop < colFirst)
  1135.                     theRect->left += theList->colDesc[loop].width;
  1136.                 theRect->right += theList->colDesc[loop].width;
  1137.             }
  1138.             
  1139.             return;
  1140.         }
  1141.         
  1142.         row2 = row2->nextRow;
  1143.         row2Count++;
  1144.     }
  1145.     
  1146.     // the row isn't visible
  1147.     theRect->top = -1;
  1148.     theRect->bottom = -1;
  1149.     theRect->left = -1;
  1150.     theRect->right = -1;
  1151. } // LLRect
  1152.  
  1153. /************************************************************/
  1154.